#include "formula.h"
#include "common.h"
#include <math.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>

#define MAX_INT32 0x7fffffff
#define MIN_INT32 -0x7fffffff

#define AVG_DEF(elist, count)                                                  \
  double sum = 0;                                                              \
  for (size_t i = 0; i < count; i++) {                                         \
    sum += (double)(*(elist + i));                                             \
  }                                                                            \
  return sum / (double)count

#define VAR_DEF(list, count, type)                                             \
  if (!list || !count) {                                                       \
    return 0;                                                                  \
  }                                                                            \
  double a = avg_##type(list, count);                                          \
  double sum = 0;                                                              \
  for (size_t i = 0; i < count; i++) {                                         \
    type x = *(list + i);                                                      \
    sum += pow(x - a, 2);                                                      \
  }                                                                            \
  return sum / (double)count

double avg_double(const double *list, size_t count) { AVG_DEF(list, count); }

double avg_uint16_t(const uint16_t *list, size_t count) {
  AVG_DEF(list, count);
}

double avg_uint32_t(const uint32_t *list, size_t count) {
  AVG_DEF(list, count);
}

double avg_uint64_t(const uint64_t *list, size_t count) {
  AVG_DEF(list, count);
}

double var_double(const double *list, size_t count) {
  VAR_DEF(list, count, double);
}

double var_i16u(const uint16_t *list, size_t count) {
  VAR_DEF(list, count, uint16_t);
}

double var_i32u(const uint32_t *list, size_t count) {
  VAR_DEF(list, count, uint32_t);
}

double deviation(const double *flist, size_t count) {
  return sqrt(var_double(flist, count));
}

double deviation_i16u(const uint16_t *ilist, size_t count) {
  return sqrt(var_i16u(ilist, count));
}

double deviation_i32u(const uint32_t *ilist, size_t count) {
  return sqrt(var_i32u(ilist, count));
}

double trim_mean(const int32_t *list, size_t count) {
  if (!list || !count)
    return 0;

  int32_t min = MAX_INT32;
  int32_t max = MIN_INT32;

  int64_t sum = 0x00;

  for (size_t i = 0; i < count; i++) {
    int32_t cur = *(list + i);

    if (cur > max)
      max = cur;
    if (cur < min)
      min = cur;

    sum += cur;
  }

  if (count <= 3)
    return (double)sum * 1.0 / (double)count;

  sum -= max;
  sum -= min;

  return (double)sum * 1.0 / (double)(count - 2);
}

uint8_t smp_hr_avg(const smp_hr_t *src_list, size_t count, smp_hr_t *dst) {
  if (!src_list || !count || !dst)
    return 0;

  uint32_t *rate_list = (uint32_t *)malloc(sizeof(uint32_t) * count);
  uint32_t *hrv_list = (uint32_t *)malloc(sizeof(uint32_t) * count);

  if (!rate_list || !hrv_list) {
    if (rate_list)
      free(rate_list);
    if (hrv_list)
      free(hrv_list);

    return 0;
  }

  uint32_t sum_motion = 0x00;

  for (uint32_t i = 0; i < count; i++) {
    const smp_hr_t *cur = src_list + i;

    *(rate_list + i) = cur->rate;
    *(hrv_list + i) = cur->hrv;

    sum_motion += cur->motion;
  }

  dst->ts = src_list->ts;
  dst->rate = (uint32_t)avg_uint32_t(rate_list, count);
  dst->hrv = (uint32_t)avg_uint32_t(hrv_list, count);
  dst->motion = sum_motion;
  dst->steps = (src_list + count - 1)->steps;

  free(rate_list);
  free(hrv_list);

  return 1;
}
